#pragma once
#ifndef __G_MATRIX_4X4_H__
#define __G_MATRIX_4X4_H__

#include "GVector3.h"
#include "GVector4.h"
#include "GQuaternion.h"

#include "GUtilities/GTypes.h"

/* Disable Anonymous Unions Warning */
#pragma warning(disable:4201)

namespace GMath
{

	class GMatrix4x4
	{
	public:
		const static GMatrix4x4	Zero;
		const static GMatrix4x4	One;
		const static GMatrix4x4	Identity;

		union 
		{
			struct
			{
				float		I11, I12, I13, I14;
				float		I21, I22, I23, I24;
				float		I31, I32, I33, I34;
				float		I41, I42, I43, I44;
			};

			float			Mat[4][4];
			float			Arr[16];
		};

							GMatrix4x4();
							GMatrix4x4(float M11, float M12, float M13, float M14,
										float M21, float M22, float M23, float M24,
										float M31, float M32, float M33, float M34,
										float M41, float M42, float M43, float M44);
			
							operator float* ();
							operator const float* () const;

		float				Determinant() const;

		bool				Inverse();
		static bool			Inverse(OUTPUT GMatrix4x4& OutMatrix, const GMatrix4x4& Matrix);

		void				Transpose();
		static void			Transpose(OUTPUT GMatrix4x4& OutMatrix, const GMatrix4x4& Matrix);

		static void			Convert(OUTPUT GMatrix4x4& OutMatrix, const GQuaternion& Quaternion);
		static void			Convert(OUTPUT GQuaternion& OutQuaternion, const GMatrix4x4& Matrix);
		
		static void			Decompose(OUTPUT GVector3& OutScale, OUTPUT GQuaternion& OutRotation, OUTPUT GVector3& OutTranslation, const GMatrix4x4& Matrix);


		static void			Multiply(OUTPUT GVector3& Output, const GVector3& Vector, const GMatrix4x4& Matrix);
		static void			Multiply(OUTPUT GVector4& Output, const GVector4& Vector, const GMatrix4x4& Matrix);
		static void			Multiply(OUTPUT GMatrix4x4& Output, const GMatrix4x4& MatrixA, const GMatrix4x4& MatrixB);

		float				operator () (GUInt32 Row, GUInt32 Column);
		float				operator () (GUInt32 Row, GUInt32 Column) const;

		GMatrix4x4&			operator  =	(const GMatrix4x4& Matrix);
		GMatrix4x4&			operator += (const GMatrix4x4& Matrix);
		GMatrix4x4&			operator -= (const GMatrix4x4& Matrix);
		GMatrix4x4&			operator *= (const GMatrix4x4& Matrix);
		GMatrix4x4&			operator *= (const float Value);
		GMatrix4x4&			operator /= (const float Value);

		GMatrix4x4			operator + (const GMatrix4x4& Matrix) const;
		GMatrix4x4			operator - (const GMatrix4x4& Matrix) const;
		GMatrix4x4			operator * (const GMatrix4x4& Matrix) const;
		GMatrix4x4			operator * (const float Value) const;
		GMatrix4x4			operator / (const float Value) const;

		GMatrix4x4			operator + () const;
		GMatrix4x4			operator - () const;

		bool				operator == (const GMatrix4x4& Matrix) const;
		bool				operator != (const GMatrix4x4& Matrix) const;

		friend GMatrix4x4	operator * (float Value, const GMatrix4x4& Matrix);
		friend GVector4		operator * (const GVector4& Vector, const GMatrix4x4& Matrix);
		friend GVector3		operator * (const GVector3& Vector, const GMatrix4x4& Matrix);
	};

}

#endif
